# pandas: 用于数据读取和处理
# numpy: 用于数值计算
# scipy.linalg: 提供线性代数工具（如线性方程、线性函数以及它们通过矩阵和向量空间的表示）
# sklearn.impute.SimpleImputer: 处理缺失值
import pandas as pd
import numpy as np
from scipy import linalg
from sklearn.impute import SimpleImputer

# 通过字典手动构造了一个包含植物名称和五个数值特征的DataFrame
# 列1存储植物名称，其余列为数值型数据
data = {
    '列1': [
        '短花针茅', '梯牧草', '蚓果芥', '柠条锦鸡儿', '小叶锦鸡儿', '冷蒿',
        '红花岩黄芪', '无芒隐子草', '荒漠锦鸡儿', '醉马草', '多根葱',
        '乳白花黄芪', '康青锦鸡儿', '灌木亚菊', '小叶锦鸡儿', '刺旋花',
        '松叶猪毛菜', '宿根亚麻', '长芒草', '银灰旋花', '山苦卖', '异叶青兰'
    ],
    '列2': [
        5.78, 2.73, 4.12, 62.00, 45.38, 1.87, 2.10, 0.00, 34.75, 34.70,
        0.00, 1.98, 6.07, 22.00, 19.25, 12.50, 17.75, 11.25, 4.67, 4.18, 0.00, 1.38
    ],
    '列3': [
        10.00, 8.00, 9.00, 0.00, 1.00, 5.00, 3.00, 0.00, 0.00, 0.00,
        5.00, 4.00, 0.00, 0.00, 0.00, 1.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00
    ],
    '列4': [
        37.33, 29.33, 21.33, 0.67, 0.00, 2.00, 5.33, 14.67, 0.00, 0.00,
        0.33, 0.67, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.33, 0.00
    ],
    '列5': [
        11.80, 7.00, 0.55, 4.10, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00,
        0.00, 0.00, 1.80, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00
    ],
    '列6': [
        6.93, 4.33, 1.30, 0.00, 0.00, 0.31, 0.17, 0.00, 0.00, 0.00,
        0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.10, 0.00
    ]
}


df = pd.DataFrame(data)

print(df)
# df.to_numpy 是 pandas 中 DataFrame 对象的一个方法，用于将 DataFrame 的数据转换为 NumPy 数组。
x = df.to_numpy()
print(x)
# 接下来的步骤与之前相同
# 标准化数据

# 确保仅选择数值列
# 仅选择数值列（列2至列6），忽略非数值列（如植物名称，列1）
# 使用.values将DataFrame转换为二维NumPy数组
numeric_cols = ['列2', '列3', '列4', '列5', '列6']
x = df[numeric_cols].values

# 处理缺失值（假设用均值填充）
# 使用SimpleImputer以均值填充缺失值（0.00会被替换为所在列的平均值）
# 确保后续计算不会因缺失值中断
im_puter = SimpleImputer(strategy='mean')
x_imputed = im_puter.fit_transform(x)

# 标准化数据
# 标准化公式：X_std = (x - μ) / σ
# μ 是均值
# σ 是标准差
epsilon = 1e-8
X = (x_imputed - np.mean(x_imputed, axis=0)) / (np.std(x_imputed, ddof=1, axis=0) + epsilon)
print("标准化数据为：")
print(X)

# 计算协方差矩阵
# 输入标准化后的数据X（形状：样本数 × 特征数）
# X.T 转置为特征数 × 样本数，符合np.cov的输入要求
# 协方差矩阵 R 反映特征间的线性相关性
R = np.cov(X.T)
print("协方差矩阵为：")
print(R)

# 计算特征值和特征向量
# 使用scipy.linalg.eigh计算实对称矩阵的特征值和特征向量
# 返回：
# eigenvalues：升序排列的特征值
# eigenvectors：对应的正交特征向量矩阵
eigenvalues, eigenvectors = linalg.eigh(R)

# 排序特征值与特征向量
# 将特征值按降序排列，确保最大特征值在前
# 同时调整特征向量的顺序，使其与特征值一一对应

# 将特征值数组按降序排列
eigenvalues = eigenvalues[::-1]
# 将特征向量矩阵的列按降序排列
eigenvectors = eigenvectors[:, ::-1]

# 计算主成分贡献率和累积贡献率
# 单个主成分的贡献率为其特征值占总特征值之和的比例，表示该主成分解释的方差比例
contribution_rate = eigenvalues / sum(eigenvalues)

# np.cumsum 是 NumPy 库中的一个函数，用于计算数组元素的累积和
# 累计贡献率是前k个主成分贡献率的累加值，用于判断保留多少主成分能覆盖大部分信息
cum_contribution_rate = np.cumsum(contribution_rate)

# 打印结果
print('特征值为：')
print(eigenvalues)
print('贡献率为：')
print(contribution_rate)
print('累计贡献率为：')
print(cum_contribution_rate)
print('与特征值对应的特征向量矩阵为：')
print(eigenvectors)
